#ifndef VL_TAG_HPP
#define VL_TAG_HPP

#include "macros.hpp"

#include <map>
#include <vector>
#include "generic_hash_set.hpp"

#error "This is not working!"

namespace vl
{

struct taggable;

struct base_tag
{	
	virtual ~base_tag()
	{
	}
	
	virtual void _deregister(taggable*) = 0;
};

struct base_tag_data
{
	virtual ~base_tag_data()
	{
	}
};

struct tag_index
{
	tag_index(taggable* k = 0, base_tag_data* v = 0)
	: k(k)
	{
	}
	
	void* key() const
	{
		return k;
	}
	
	base_tag_data const& value() const
	{
		return *v;
	}
	
	bool is_empty() const
	{
		return !k;
	}
	
	void make_empty()
	{
		if(k)
		{
			delete v;
			v = 0;
			k = 0;
		}
	}
	
	void assign_value(tag_index idx)
	{
		delete k;
		k = idx.k;
	}
	
	taggable* k;
	base_tag_data* v;
};

template<typename T, typename DataT>
struct tag : base_tag
{
	//typedef std::map<void*, base_tag_data*> assoc_type;
	typedef vl::generic_hash_set<tag_index, taggable*, base_tag_data, hash_functor, std::equal<taggable*>, true> assoc_type;
	
	void assign(T* key, DataT* data)
	{
		taggable* k = key;
		
		assoc.insert(tag_index(k, data));
		key->_tags.push_back(this);
	}
	
	DataT& operator[](T* v);
	
	virtual void _deregister(taggable* k)
	{
		assoc.remove(k);
	}
	
	virtual ~tag();
	
	assoc_type assoc;
};

struct taggable
{
	typedef std::vector<base_tag*> tag_list;
	
	virtual ~taggable()
	{
		FOREACH(tag_list, i, _tags)
		{
			(*i)->_deregister(this);
		}
	}
	
	tag_list _tags;
};

template<typename T, typename DataT>
tag<T, DataT>::~tag()
{
	FOREACH(assoc_type, i, assoc)
	{
		taggable::tag_list& tags = static_cast<taggable*>(i->first)->_tags;
		
		FOREACH(taggable::tag_list, j, tags)
		{
			if(*j == this)
			{
				*j = tags.back();
				tags.resize(tags.size() - 1);
				break;
			}
		}
	}
}

template<typename T, typename DataT>
DataT& tag<T, DataT>::operator[](T* key)
{
	taggable* k = key;
	tag_index* idx = assoc.lookup(k);
	if(idx)
		return *static_cast<DataT*>(idx->v->second);
	
	DataT* data = new DataT;
	assoc.insert(tag_index(k, data));
	key->_tags.push_back(this);
	return *data;
}

}

#endif //VL_TAG_HPP
